{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2-2 三种计算图"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有三种计算图的构建方式：**静态计算图**，**动态计算图**，**以及Autograph**.\n",
    "\n",
    "在TensorFlow1.0时代，采用的是静态计算图，需要先使用TensorFlow的各种算子创建计算图，然后再开启一个会话Session，显式执行计算图。\n",
    "\n",
    "而在TensorFlow2.0时代，采用的是动态计算图，即每使用一个算子后，该算子会被动态加入到隐含的默认计算图中立即执行得到结果，而无需开启Session。\n",
    "\n",
    "使用动态计算图即Eager Excution的好处是方便调试程序，它会让TensorFlow代码的表现和Python原生代码的表现一样，写起来就像写numpy一样，各种日志打印，控制流全部都是可以使用的。\n",
    "\n",
    "使用动态计算图的缺点是运行效率相对会低一些。因为使用动态图会有许多次Python进程和TensorFlow的C++进程之间的通信。而静态计算图构建完成之后几乎全部在TensorFlow内核上使用C++代码执行，效率更高。此外静态图会对计算步骤进行一定的优化，剪去和结果无关的计算步骤。\n",
    "\n",
    "如果需要在TensorFlow2.0中使用静态图，**可以使用@tf.function装饰器将普通Python函数转换成对应的TensorFlow计算图构建代码**。运行该函数就相当于在TensorFlow1.0中用Session执行代码。\n",
    "> 使用tf.function构建静态图的方式叫做 *Autograph*."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 一，计算图简介\n",
    "\n",
    "计算图由节点(nodes)和线(edges)组成。\n",
    "\n",
    "节点表示操作符Operator，或者称之为算子，线表示计算间的依赖。\n",
    "\n",
    "实线表示有数据传递依赖，传递的数据即张量。\n",
    "\n",
    "虚线通常可以表示控制依赖，即执行先后顺序。\n",
    "\n",
    "![](./data/strjoin_graph.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 二，静态计算图\n",
    "\n",
    "在TensorFlow1.0中，使用静态计算图分两步，第一步定义计算图，第二步在会话中执行计算图。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**TensorFlow 1.0静态计算图范例**\n",
    "\n",
    "```python\n",
    "import tensorflow as tf\n",
    "\n",
    "#定义计算图\n",
    "g = tf.Graph()\n",
    "with g.as_default():\n",
    "    #placeholder为占位符，执行会话时候指定填充对象\n",
    "    x = tf.placeholder(name='x', shape=[], dtype=tf.string)  \n",
    "    y = tf.placeholder(name='y', shape=[], dtype=tf.string)\n",
    "    z = tf.string_join([x,y],name = 'join',separator=' ')\n",
    "\n",
    "#执行计算图\n",
    "with tf.Session(graph = g) as sess:\n",
    "    print(sess.run(fetches = z,feed_dict = {x:\"hello\",y:\"world\"}))\n",
    "   \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**TensorFlow2.0 怀旧版静态计算图**\n",
    "\n",
    "TensorFlow2.0为了确保对老版本tensorflow项目的兼容性，在tf.compat.v1子模块中保留了对TensorFlow1.0那种静态计算图构建风格的支持。\n",
    "\n",
    "可称之为怀旧版静态计算图，已经不推荐使用了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'hello world'\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "\n",
    "g = tf.compat.v1.Graph()\n",
    "with g.as_default():\n",
    "    x = tf.compat.v1.placeholder(name='x', shape=[], dtype=tf.string)\n",
    "    y = tf.compat.v1.placeholder(name='y', shape=[], dtype=tf.string)\n",
    "    z = tf.strings.join([x, y], name='join', separator=\" \")\n",
    "    \n",
    "with tf.compat.v1.Session(graph=g) as sess:\n",
    "    # fetches的结果非常像一个函数的返回值，而feed_dict中的占位符相当于函数的参数序列。\n",
    "    result = sess.run(fetches=z, feed_dict={x:\"hello\", y:\"world\"})\n",
    "    print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 三，动态计算图"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在TensorFlow2.0中，使用的是动态计算图和Autograph.\n",
    "\n",
    "在TensorFlow1.0中，使用静态计算图分两步，第一步定义计算图，第二步在会话中执行计算图。\n",
    "\n",
    "动态计算图已经不区分计算图的定义和执行了，而是定义后立即执行。因此称之为 Eager Excution.\n",
    "\n",
    "Eager这个英文单词的原意是\"迫不及待的\"，也就是立即执行的意思。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(b'hello world', shape=(), dtype=string)\n",
      "hello world\n"
     ]
    }
   ],
   "source": [
    "# 动态计算图在每个算子处都进行构建，构建后立即执行\n",
    "x = tf.constant(\"hello\")\n",
    "y = tf.constant(\"world\")\n",
    "z = tf.strings.join([x, y], separator=\" \")\n",
    "print(z)\n",
    "tf.print(z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n",
      "tf.Tensor(b'hello world', shape=(), dtype=string)\n"
     ]
    }
   ],
   "source": [
    "# 可将动态计算图代码的输入和输出关系封装成函数\n",
    "\n",
    "def strjoin(x, y):\n",
    "    z = tf.strings.join([x, y], separator=\" \")\n",
    "    tf.print(z)\n",
    "    return z\n",
    "\n",
    "result = strjoin(tf.constant(\"hello\"), tf.constant(\"world\"))\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 四 Tensorflow2.0的 Autograph\n",
    "\n",
    "动态计算图运行效率相对较低。\n",
    "\n",
    "可以用@tf.function装饰器将普通Python函数转换成和TensorFlow1.0对应的静态计算图构建代码。\n",
    "\n",
    "在TensorFlow1.0中，使用计算图分两步，第一步定义计算图，第二步在会话中执行计算图。\n",
    "\n",
    "在TensorFlow2.0中，如果采用Autograph的方式使用计算图，第一步定义计算图变成了定义函数，第二步执行计算图变成了调用函数。\n",
    "不需要使用会话了，一些都像原始的Python语法一样自然。\n",
    "\n",
    "实践中，我们一般会先用动态计算图调试代码，然后在需要提高性能的的地方利用@tf.function切换成Autograph获得更高的效率。\n",
    "\n",
    "当然，@tf.function的使用需要遵循一定的规范，我们后面章节将重点介绍。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n",
      "tf.Tensor(b'hello world', shape=(), dtype=string)\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "\n",
    "# 使用autograph构建静态图\n",
    "\n",
    "@tf.function\n",
    "def strjoin(x, y):\n",
    "    z = tf.strings.join([x, y], separator=\" \")\n",
    "    tf.print(z)\n",
    "    return z\n",
    "\n",
    "result = strjoin(tf.constant(\"hello\"),tf.constant(\"world\"))\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n"
     ]
    }
   ],
   "source": [
    "import datetime\n",
    "\n",
    "# 创建日志\n",
    "stamp = datetime.datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
    "logdir = \"./data/autograph/%s\" % stamp\n",
    "writer = tf.summary.create_file_writer(logdir)\n",
    "\n",
    "# 开启 autograph跟踪\n",
    "tf.summary.trace_on(graph=True, profiler=True)\n",
    "\n",
    "# 执行 autograph\n",
    "result = strjoin(\"hello\", \"world\")\n",
    "\n",
    "#将计算图信息写入日志\n",
    "with writer.as_default():\n",
    "    tf.summary.trace_export(\n",
    "        name=\"autograph\",\n",
    "        step=0,\n",
    "        profiler_outdir=logdir\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The tensorboard extension is already loaded. To reload it, use:\n",
      "  %reload_ext tensorboard\n"
     ]
    }
   ],
   "source": [
    "#启动 tensorboard在jupyter中的魔法命令\n",
    "%load_ext tensorboard"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Reusing TensorBoard on port 6007 (pid 29609), started 0:03:36 ago. (Use '!kill 29609' to kill it.)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "      <iframe id=\"tensorboard-frame-412ce97002f47e77\" width=\"100%\" height=\"800\" frameborder=\"0\">\n",
       "      </iframe>\n",
       "      <script>\n",
       "        (function() {\n",
       "          const frame = document.getElementById(\"tensorboard-frame-412ce97002f47e77\");\n",
       "          const url = new URL(\"/\", window.location);\n",
       "          url.port = 6007;\n",
       "          frame.src = url;\n",
       "        })();\n",
       "      </script>\n",
       "  "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#启动tensorboard\n",
    "%tensorboard --logdir ./data/autograph/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
